/**
 * 封装各类 API 调用函数
 */
import type { MaySuccess } from '@gnlab/utils'
import { getLogger } from '@gnlab/utils'
import config from '@/config'

const logger = getLogger('services/api')

// ========================
// 配套函数
// ========================

/**
 * 设置请求时附带的 token
 */
let token = ''
export function setToken(_token: string) {
  token = _token
}

/**
 * 设置判定未登录时的回调
 */
let unauthorizedCallback = (message?: string) => logger.warn('unauthorized') // eslint-disable-line @typescript-eslint/no-unused-vars
export function onUnauthorized(callback: (message?: string) => void) {
  unauthorizedCallback = callback
}

// ========================
// API 调用
// ========================

interface RequestParams<T = unknown> extends RequestInit {
  data?: T
}
type Response<ResultT, ErrorT = string> = Promise<MaySuccess<ResultT, ErrorT>>

function join(baseURL: string, append: string) {
  if (baseURL.endsWith('/')) baseURL = baseURL.slice(0, baseURL.length - 1)
  if (append.startsWith('/')) append = append.slice(1)
  return `${baseURL}/${append}`
}

type AppsSuccess<T> =
  | { code: 0; msg: ''; data: T }
  | { errcode: 0; errmsg: ''; data: T }
  | { code: 0; msg: ''; list: T }
  | { errcode: 0; errmsg: ''; list: T }
type AppsFailed =
  | { code: -1; msg: string; data: null }
  | { errcode: number; errmsg: string; data?: null }
type AppsResponse<T> = AppsSuccess<T> | AppsFailed

// 流量超市 API 公共函数
async function appsAPICommon<Res, Para = unknown>(
  url: string,
  params: RequestParams<Para> = {},
): Response<Res, { message: string; code?: number }> {
  const { data: reqData, headers, ...restParams } = params
  const body = typeof reqData !== 'undefined' ? JSON.stringify(reqData) : reqData

  let resp: AppsResponse<Res>
  try {
    const request = await window.fetch(url, {
      ...restParams,
      ...(body !== undefined ? { body } : {}),
      headers: {
        ...(token ? { token } : {}),
        ...(body !== undefined ? { 'Content-Type': 'application/json;charset=UTf-8' } : {}),
        ...(headers ?? {}),
      },
    })
    resp = (await request.json()) as AppsResponse<Res>
  } catch (error) {
    logger.warn('apps-api-failed', url, error)
    return { success: false, error: { message: '系统错误' } }
  }
  if ('code' in resp && resp.code !== 0) {
    return { success: false, error: { code: resp.code, message: resp.msg || '系统错误' } }
  } else if ('errcode' in resp && resp.errcode !== 0) {
    if (resp.errcode === 500 && resp.errmsg === '用户尚未登录') {
      unauthorizedCallback()
      return { success: false, error: { code: resp.errcode, message: '未登录' } }
    }
    return { success: false, error: { code: resp.errcode, message: resp.errmsg || '系统错误' } }
  }

  if ('list' in resp) return { success: true, data: resp.list }
  else return { success: true, data: resp.data! }
}

/**
 * 调用流量超市 Account API
 * 仅在 AuthMethod 为 apps 时才有可能调用此接口
 */
export async function appsAccountAPI<Res, Para = unknown>(
  path: string,
  params: RequestParams<Para> = {},
) {
  return appsAPICommon<Res, Para>(join(config.APPS_ACCOUNT_API, path), params)
}

/**
 * 调用流量超市业务 API
 * 仅在 AuthMethod 为 apps 时才有可能调用此接口
 */
export async function appsAPI<Res, Para = unknown>(path: string, params: RequestParams<Para> = {}) {
  return appsAPICommon<Res, Para>(join(config.APPS_API, path), params)
}

/**
 * 调用后端 API（C#）
 */
type APISuccess<T> = { code: 0; msg: '成功'; data: T }
type APIFailed = { code: -1 | -2 | -3; msg: string; data: null }
type APIResponse<T> = APISuccess<T> | APIFailed
type APIParams<T> = RequestParams<T> & {
  ignoreUnauthorized?: boolean // 若未 true，忽略 unauthorized 的情况，不触发 unauthorizedCallback
  token?: string // 手动指定 token
}
type APIParamsWithFormatter<T, RespDataT, FormattedT> = APIParams<T> & {
  formatter: (raw: RespDataT) => FormattedT
}

function API<Data, Para = unknown>(path: string, params?: APIParams<Para>): Response<Data>
function API<Data, Para = unknown, RespData = unknown>(
  path: string,
  params?: APIParamsWithFormatter<Para, RespData, Data>,
): Response<Data>
async function API<Data, Para = unknown, RespData = unknown>(
  path: string,
  params: APIParams<Para> | APIParamsWithFormatter<Para, RespData, Data> = {},
): Response<Data> {
  const url = join(config.API, path)
  const {
    data: reqData,
    headers,
    ignoreUnauthorized = false,
    token: reqToken,
    ...restParams
  } = params
  const body = reqData !== undefined ? JSON.stringify(reqData) : params.body
  const usedToken = (token || reqToken) ?? ''

  let resp: APIResponse<unknown>
  try {
    const request = await window.fetch(url, {
      method: 'POST',
      ...restParams,
      ...(body !== undefined ? { body } : {}),
      headers: {
        ...(usedToken ? { token: usedToken } : {}),
        ...(body !== undefined && !(body instanceof FormData)
          ? { 'Content-Type': 'application/json;charset=utf-8' }
          : {}),
        ...(headers ?? {}),
      },
    })
    resp = (await request.json()) as APIResponse<unknown>
  } catch (error) {
    logger.warn('system-error', url, error)
    return { success: false, error: '系统错误' }
  }

  if (resp.code === 0) {
    const data =
      'formatter' in params ? params.formatter(resp.data as RespData) : (resp.data as Data)
    return { success: true, data }
  } else if (resp.code === -2) {
    if (!ignoreUnauthorized) {
      unauthorizedCallback(resp.msg)
    }
    return { success: false, error: resp.msg || '未登录' }
  } else {
    logger.warn('api-failed', path, resp.msg)
    return { success: false, error: resp.msg }
  }
}
export { API }
